﻿using Microsoft.Extensions.Logging;
using RushMail.Requests;

namespace RushMail;

public class RushMailClient : IRushMailClient
{
    internal ILogger<RushMailClient>? Logger { get; private  set; }
    private readonly string _customId;
    private readonly string _secretKey;
    private readonly HttpClient _client;
    
    public PoolCategory PoolCategory { get; }
    public Pool Pool { get; }
    public PoolAddress PoolAddress { get; }
    public TemplateCategory TemplateCategory { get; }
    public Template Template{ get; }
    public Domain Domain{ get; }
    public DomainEmail DomainEmail{ get; }
    public Track Track{ get; }
    public Account Account{ get; }
    public Report Report{ get; }
    public RushMailTask Task{ get; }

    public RushMailClient(string customId, string secretKey, HttpMessageHandler? httpMessageHandler = null, ILogger<RushMailClient>? logger = null)
    {
        Logger = logger;
        _customId = !string.IsNullOrEmpty(customId) ? customId : throw new ArgumentNullException(customId);
        _secretKey = !string.IsNullOrEmpty(secretKey) ? secretKey : throw new ArgumentNullException(secretKey);
        _client = InitHttpClient(httpMessageHandler);

        PoolCategory = new PoolCategory(this);
        Pool = new Pool(this);
        PoolAddress = new PoolAddress(this);
        TemplateCategory = new TemplateCategory(this);
        Template = new Template(this);
        Domain = new Domain(this);
        DomainEmail = new DomainEmail(this);
        Track = new Track(this);
        Account = new Account(this);
        Report = new Report(this);
        Task = new RushMailTask(this);
    }

    private HttpClient InitHttpClient(HttpMessageHandler? httpMessageHandler)
    {
        var client = httpMessageHandler is null ? new HttpClient() : new HttpClient(httpMessageHandler);
        client.BaseAddress = new Uri("http://edmapi.rushmail.com/api.php");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.UserAgent.ParseAdd("rushmail-api-net/1.0.0");
        return client;
    }
    
    public async Task<RushMailResponse> PostAsync(RushMailRequest request)
    {
        var body = request.Body ?? new ();
        body["customer_id"] = _customId;
        body["token"] = GetSign();
        var response = await _client.PostAsync(request.BuildUrl(), new FormUrlEncodedContent(body)).ConfigureAwait(false);
        
        var result = await BuildResponse(response).ConfigureAwait(false);
        Logger?.LogInformation("{Url} {CustomId} {Code}\n{Response}", request.BuildUrl(), _customId, result.StatusCode, result.Content.ToString(Formatting.None));
        response = null;
        return result;
    }

    private async Task<RushMailResponse> BuildResponse(HttpResponseMessage response)
    {
        JObject result;
        var responseStr = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        try
        {
            result = JObject.Parse(responseStr);
        }
        catch
        {
            result = new JObject();
            result["status"] = false;
            result["data"] = 0;
            result["invalid_response"] = responseStr;
        }

        // 一些新增类的接口，如新增地址池、添加群发任务等，如果调用成功，， 则 status 会等于 id，如地址池 id、群发任务 id。
        if (result.TryGetValue("status", out var token) && token.Type != JTokenType.Boolean)
        {
            var id = token.Type switch
            {
                JTokenType.String => Convert.ToInt32(token.Value<string>()),
                JTokenType.Integer => token.Value<int>(),
                _ => throw new ArgumentOutOfRangeException()
            };
            result["status"] = true;
            result["data"] = id;
        }

        return new RushMailResponse(response.IsSuccessStatusCode, (int)response.StatusCode, result);
    }

    private string GetSign()
    {
        var currentHour = DateTime.UtcNow.AddHours(8).ToString("yyyy-MM-dd-HH");
        return RushMailHelper.Md5($"{_customId}|{_secretKey}|{currentHour}");
    }
}